﻿using System.Security.Cryptography;
using Dark_Core_Server.Models.Article;
using Dark_Core_Server.Models.File;
using DarkSKL_Core.ArgModules;
using DarkSKL_Core.ConfigModules;
using DarkSKL_Core.MongoDBModules.Helper;   
using DarkSKL_Core.MongoDBModules.Models;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Serilog;
using System.Text;
using COSXML;
using COSXML.Auth;
using COSXML.Common;
using COSXML.Model.Bucket;
using COSXML.Model.Object;
using COSXML.Model.Service;
using COSXML.Model.Tag;
using COSXML.Network;
using COSXML.Transfer;
using Dark_Core_Server.Models.Universal;
using Dark_Core_Server.Models.User;
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.OpenApi.Models;
using MongoDB.Bson;
using CommunityToolkit.Mvvm.Messaging;

namespace Dark_Core_Server.Service
{
    /// <summary>
    /// 文件服务
    /// </summary>
    public class FileService
    {
        private readonly string BasePath;
        private readonly string RelativeDictionaryPath;
        private readonly IWebHostEnvironment ENV; 
        private readonly IMongoCollection<FileDocument> Collection;
        private readonly IMongoCollection<FileReferenceDocument> ReferenceCollection;
        private readonly string CosAppID;
        private readonly CosXmlConfig CosXmlConfig;
        private readonly QCloudCredentialProvider CosCredentialProvider;
        private readonly CosXmlServer CosXmlService;


        public FileService(IWebHostEnvironment env)
        {
            this.ENV = env;
            string tPath = Path.Combine(ENV.ContentRootPath);
            tPath = Environment.OSVersion.Platform == PlatformID.Win32NT ? ENV.ContentRootPath : ENV.ContentRootPath.Replace("\\", "/");
            this.RelativeDictionaryPath =
                EnvironmentConfigController.ConfigController.GetConfig<string>("FILESERVICE:RELATIVE_PATH") ??
                "upload";
            BasePath = Path.Combine(tPath,RelativeDictionaryPath
                 );
            // 读取数据库配置
            DatabaseConfig config = new DatabaseConfig()
            {
                DatabaseName = EnvironmentConfigController
                    .ConfigController
                    .GetConfig<string>("DB:FILE_DB_NAME"),
                User = EnvironmentConfigController.ConfigController.GetConfig<string>("DB:USER"),
                Password = EnvironmentConfigController
                    .ConfigController
                    .GetConfig<string>("DB:PASS"),
                Host = EnvironmentConfigController.ConfigController.GetConfig<string>("DB:HOST"),
                Port = EnvironmentConfigController.ConfigController.GetConfig<int>("DB:PORT"),
                Args = EnvironmentConfigController.ConfigController.GetConfig<string>("DB:ARGS")

            };

            // 文章集合 (名称来自配置或使用 "basicUnit" 作为默认)
            Collection = MongodbBuilder.CreateCollection<FileDocument>(
                config,
                EnvironmentConfigController
                    .ConfigController
                    .GetConfig<string>("DB:FILE_DB_COLLECTION") ?? "basicUnit"
            );

            // 评论集合 (示例名称为 "Comment"，可根据项目需要修改)
            ReferenceCollection = MongodbBuilder.CreateCollection<FileReferenceDocument>(
                config,
                EnvironmentConfigController
                    .ConfigController
                    .GetConfig<string>("DB:FILE_DB_PROFILE_COLLECTION") ?? "commentUnit"
            );

            // COS APP ID
            CosAppID = EnvironmentConfigController.ConfigController.GetConfig<string>("COS:APP_ID") ?? "1324532138";

            // 初始化 COS 配置
            CosXmlConfig = new CosXmlConfig.Builder().IsHttps(true)
                .SetRegion(EnvironmentConfigController.ConfigController.GetConfig<string>("COS:COS_REGION"))
                .SetDebugLog(true).Build();

            // 构建云证书提供商
            //TODO:这里的配置过期时间 是按照一年设计的，超过一年需要手动重启 或者你自己改
            CosCredentialProvider = new DefaultQCloudCredentialProvider(
                EnvironmentConfigController.ConfigController.GetConfig<string>("COS:SECRET_ID"),
                EnvironmentConfigController.ConfigController.GetConfig<string>("COS:SECRET_KEY"),
                EnvironmentConfigController.ConfigController.GetConfig<int>("COS:DURATION_SECONDS")
            );
            // 构建 COS 服务
            CosXmlService = new CosXmlServer(CosXmlConfig, CosCredentialProvider);


            // 用户删除时，自动删除其引用的文件记录
            WeakReferenceMessenger
                .Default
                .Register<UserDocument, string>(
                    this,
                    "UserDeleted",
                    UserDeletedHandler
                );

        }

        #region Event Handlers
        private async void UserDeletedHandler(object recipient, UserDocument message)
        {
            await ReferenceCollection.DeleteManyAsync(x => x.UserId == message.id);
        }


        #endregion

        #region File Modules

        /// <summary>
        /// 保存文件异步
        /// </summary>
        /// <remarks>
        /// 如果文件MD5值存在，则直接返回文件信息
        /// 否则，保存文件到本地，计算MD5值，保存到数据库，返回文件信息 保存至COS
        /// </remarks>
        /// <param name="file">文件对象</param>
        /// <returns></returns>
        public async Task<ResponseMsg<FileDocument>> SaveFileAsync(IFormFile file)
        {
            
            string firstPath = file.ContentType.Split("/")[0];
            string secondlyPath = file.ContentType.Split("/")[1];
            string relativeSaveDirectoryPath = Path.Combine(firstPath, secondlyPath);
            string absoluteSaveDirectoryPath = Path.Combine(BasePath, relativeSaveDirectoryPath);
            string savedFilePath = Path.Combine(absoluteSaveDirectoryPath, file.FileName);
            string fileMD5 = string.Empty;
            if (!Directory.Exists(absoluteSaveDirectoryPath)) Directory.CreateDirectory(absoluteSaveDirectoryPath);
            try
            {
                using (var md5 = MD5.Create())
                {
                    // 打开目标文件流
                    await using (var fileStream = new FileStream(savedFilePath, FileMode.Create))
                    // 构造 CryptoStream，使数据写入 fileStream 同时计算 MD5
                    await using (var cryptoStream = new CryptoStream(fileStream, md5, CryptoStreamMode.Write))
                        await file.CopyToAsync(cryptoStream);

                    // 数据写入并结束后，从 md5.Hash 获取哈希字节数组，
                    // 转换为 32 位小写十六进制字符串
                    if (md5.Hash != null) fileMD5 = BitConverter.ToString(md5.Hash).Replace("-", "").ToLowerInvariant();
                }



                var findResult = await QueryFileListAsync(FileQueryMode.MD5, fileMD5);
                if (findResult.Data.Total > 0) return findResult.Data.Rows[0].CreateResponseMsg("文件已存在，无需重复上传",UniversalState.Success);

                //var findResult = await (await Collection.FindAsync(x => x.MD5 == fileMD5)).ToListAsync();
                //if (findResult.Count > 0)
                //{
                //    return findResult[0].CreateResponseMsg("文件已存在，无需重复上传", UniversalState.Success);
                //}


                //TODO:虽然这种方式很蠢，但是行之有效
                // 每次保存文件前先检查是否存在 bucket，不存在则创建
                string bucket = $"{firstPath}-{CosAppID}";
                DoesBucketExistRequest existsRequest = new DoesBucketExistRequest(bucket);
                bool isExistsBucket = CosXmlService.DoesBucketExist(existsRequest);
                if (!isExistsBucket)
                {
                    try
                    {
                        PutBucketRequest createRequest = new PutBucketRequest(bucket);
                        createRequest.SetCosACL("public-read");
                        PutBucketResult result = CosXmlService.PutBucket(createRequest);
                    }
                    catch (COSXML.CosException.CosClientException clientEx)
                    {
                        Console.WriteLine("CosClientException.: " + clientEx);
                    }
                    catch (COSXML.CosException.CosServerException serverEx)
                    {
                        Console.WriteLine("CosServerException: " + serverEx.GetInfo());
                    }
                }



                FileDocument fileDocument = new()
                {
                    OriginUrl = Path.Combine(RelativeDictionaryPath, relativeSaveDirectoryPath, file.FileName).Replace("\\", "/"),
                    Size = file.Length,
                    Type = file.ContentType,
                    MD5 = fileMD5
                };



                var tempFileSplit = file.FileName.Split('.');
                var extenstion = secondlyPath;
                if (tempFileSplit.Length > 1) extenstion = tempFileSplit[^1];


                TransferConfig transferConfig = new TransferConfig();
                TransferManager transferManager = new TransferManager(CosXmlService, transferConfig);
                string cosObjectKey = $"{fileDocument.id.ToString()}.{extenstion}";
                string srcPath = savedFilePath;

                COSXMLUploadTask uploadTask = new COSXMLUploadTask(bucket, cosObjectKey);
                uploadTask.SetSrcPath(srcPath);
                // 上传文件至COS中
                try
                {
                    COSXMLUploadTask.UploadTaskResult result = await transferManager.UploadAsync(uploadTask);
                    string eTag = result.eTag;
                    fileDocument.COSETag = eTag;
                }
                catch (Exception e)
                {
                    Log.Error("UploadAsync Exception: " + e.Message);
                    throw;
                }

                // 获取文件的COS URL地址
                try
                {
                    string cosUrl = CosXmlService.GetObjectUrl(bucket, cosObjectKey);
                    fileDocument.COSUrl = cosUrl;
                }
                catch (Exception e)
                {
                    Log.Error("GetObjectUrl Exception: " + e.Message);
                    throw;
                }

                // 补全信息
                fileDocument.COSBucket = bucket;
                fileDocument.COSObjectKey = cosObjectKey;
                await Collection.InsertOneAsync(fileDocument);
                return fileDocument.CreateResponseMsg("文件上传成功", UniversalState.Success);
            }
            catch (Exception e)
            {
                Log.Error($"FileService.SaveFileAsync Exception:{e.Message}");
                return new FileDocument() { id = ObjectId.Empty }.CreateResponseMsg($"FileService.SaveFileAsync Exception:{e.Message} ", UniversalState.Error);
            }






        }

        /// <summary>
        /// 查询文件列表异步
        /// </summary>
        /// <param name="mode">查询模式</param>
        /// <param name="arg">查询参数</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public async Task<ResponseMsg<DataArrResponse<FileDocument>>> QueryFileListAsync(
            FileQueryMode mode,
            string arg
        )
        {
            try
            {

                var filter = mode switch
                {
                    FileQueryMode.ALL => Builders<FileDocument>.Filter.Empty,
                    FileQueryMode.MD5 => Builders<FileDocument>.Filter.Eq(x => x.MD5, arg),
                    FileQueryMode.COSObjectKey => Builders<FileDocument>.Filter.Eq(x => x.COSObjectKey, arg),
                    FileQueryMode.ID => Builders<FileDocument>.Filter.Eq(x => x.id, ObjectId.Parse(arg)),
                    _ => throw new ArgumentException("未知查询模式")
                };
                //TODO:这里或许要做分页查询，因为有全部ALL查询的时候，可能会有大量数据，导致查询时间过长
                var findResult = await (await Collection.FindAsync(filter)).ToListAsync();
                return findResult.ToDataArrResponse().CreateResponseMsg("查询成功", UniversalState.Success);

            }
            catch (Exception e)
            {
                Log.Error($" FileService.QueryFileListAsync Exception:{e.Message}");
                return Array.Empty<FileDocument>().ToDataArrResponse()
                    .CreateResponseMsg($"FileService.QueryFileListAsync Exception:{e.Message}", UniversalState.Error);
            }

        }
        

        /// <summary>
        /// 删除文件异步
        /// </summary>
        /// <param name="id">文件ID</param>
        /// <returns></returns>
        public async Task<ResponseMsg<bool>> DeleteFileAsync(ObjectId id)
        {
            try
            {
                var findResult = await (QueryFileListAsync(FileQueryMode.ID, id.ToString()));
                if (findResult.Data.Total < 1) throw new Exception("文件不存在");
                var fileDocument = findResult.Data.Rows[0];
                // 删除文件文档
                var deleteResult = await Collection.DeleteOneAsync(x => x.id == fileDocument.id);
                if (deleteResult.DeletedCount < 1) throw new Exception("Mongodb文档删除失败");
                // 删除引用文档
                await ReferenceCollection.DeleteManyAsync(x => x.FileId == fileDocument.id);
                
                // 删除COS文件
                try
                {
                    DeleteObjectRequest request =
                        new DeleteObjectRequest(fileDocument.COSBucket, fileDocument.COSObjectKey);
                    DeleteObjectResult result = CosXmlService.DeleteObject(request);
                }
                catch (COSXML.CosException.CosClientException clientEx)
                {
                    Console.WriteLine("COS Delete ClientException: " + clientEx);
                }
                catch (COSXML.CosException.CosServerException serverEx)
                {
                    Console.WriteLine("COS Delete ServerException:: " + serverEx.GetInfo());
                }

                return true.CreateResponseMsg("文件删除成功", UniversalState.Success);


            }
            catch (Exception e)
            {
                Log.Error($"FileService.DeleteFileAsync Exception:{e.Message}");
                return false.CreateResponseMsg("文件删除失败", UniversalState.Error);
            }
        }

        #endregion


        #region File Reference Modules


        /// <summary>
        /// 查询引用关系
        /// </summary>
        /// <param name="mode">查询模式</param>
        /// <param name="args">查询参数</param>
        /// <returns></returns>
        public async Task<ResponseMsg<DataArrResponse<FileReferenceDocument>>> QueryReferenceAsync(FileReferenceQueryMode mode, string[] args)
        {
            try
            {
                var filter = mode switch
                {
                    FileReferenceQueryMode.ID => Builders<FileReferenceDocument>.Filter.Eq(x => x.id,
                        ObjectId.Parse(args[0])),
                    FileReferenceQueryMode.UserID => Builders<FileReferenceDocument>.Filter.Eq(x => x.UserId,
                        ObjectId.Parse(args[0])),
                    FileReferenceQueryMode.UserIDAndFileName => Builders<FileReferenceDocument>.Filter.And(
                        Builders<FileReferenceDocument>.Filter.Eq(x => x.UserId, ObjectId.Parse(args[0])),
                        Builders<FileReferenceDocument>.Filter.Regex(x => x.FileName,
                            new BsonRegularExpression(args[1], "i"))),
                    FileReferenceQueryMode.UserIDAndFileType => Builders<FileReferenceDocument>.Filter.And(
                        Builders<FileReferenceDocument>.Filter.Eq(x => x.UserId, ObjectId.Parse(args[0])),
                        Builders<FileReferenceDocument>.Filter.Eq(x => x.FileExtenstion, args[1])),
                    _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
                };

                var result = (await ReferenceCollection.FindAsync(filter)).ToList();
                return result.ToDataArrResponse().CreateResponseMsg("查询成功",UniversalState.Success);

            }
            catch (Exception e)
            {
                Log.Error("FileService.QueryReferenceAsync Exception: "+e.Message);
                return Array.Empty<FileReferenceDocument>().ToDataArrResponse()
                    .CreateResponseMsg($"FileService.QueryReferenceAsync Exception: {e.Message}", UniversalState.Error);
            }
        }

        /// <summary>
        /// 基于MD5添加引用关系
        /// </summary>
        /// <returns></returns>
        public async Task<ResponseMsg<FileReferenceDocument>> AddReferenceAsync(ObjectId userId, SimpleFileReferenceRecord record)
        {
            try
            {
                var findResult = await (await Collection.FindAsync(x => x.MD5 == record.MD5)).ToListAsync();
                if (findResult.Count < 1) throw new Exception("文件不存在");

                FileReferenceDocument referenceDocument = record.ToReferenceDocumentDocument(userId, findResult[0].id);
                await ReferenceCollection.InsertOneAsync(referenceDocument);
                return referenceDocument.CreateResponseMsg("文件引用关系创建成功", UniversalState.Success);
            }
            catch (Exception e)
            {
                Log.Error("FileService.AddReferenceAsync Exception: " + e.Message);
                return FileReferenceDocument.Empty.CreateResponseMsg(
                    "FileService.AddReferenceAsync Exception: " + e.Message, UniversalState.Error);
            }
        }


        /// <summary>
        /// 移除引用关系
        /// </summary>
        /// <param name="user">用户</param>
        /// <param name="referenceId">引用ID</param>
        /// <returns></returns>
        public async Task<ResponseMsg<bool>> RemoveReferenceAsync(ObjectId userId, ObjectId referenceId)
        {
            try
            {
                var deleteResult = await ReferenceCollection.DeleteOneAsync(x => x.id == referenceId && x.UserId == userId);
                if (deleteResult.DeletedCount < 1) throw new Exception("引用关系不存在");
                return true.CreateResponseMsg("引用关系删除成功", UniversalState.Success);
            }
            catch (Exception e)
            {
                Log.Error("FileService.RemoveReferenceAsync Exception: " + e.Message);
                return false.CreateResponseMsg("FileService.RemoveReferenceAsync Exception: " + e.Message,
                    UniversalState.Error);


            }
        }


        #endregion








    }
}
